
#include "zen_vap_figure.h"
#include "zen_vap_activity.h"

using namespace std;
namespace Zen { namespace Vap {
	//	void _sFixCoords(std::vector<VertexData> & to, std::vector<VertexData> const & src,
	//					 Vec2 const & scale)
	//	{
	//
	//	}

	FigurePoints::FigurePoints()
	{
		this->initR_();
	}
	
	void FigurePoints::setTexture(SharedTexture texture)
	{
		m_texture = texture;
	}
	SharedTexture FigurePoints::getTexture()
	{
		return m_texture;
	}
	void FigurePoints::setPointSizeScale(float scale)
	{
		if(m_point_scale == scale) return;
		m_point_scale = scale;
		this->resetSizeRatio_();
	}

	float FigurePoints::getPointSizeScale()
	{
		return m_point_scale;
	}
	void FigurePoints::updateContent()
	{
		auto vs = Activity::Get()->getRealViewSize();

		if(this->m_scale2.x != vs.w)
		{
			this->setScale2_(vs.w, vs.h);
			this->resetSizeRatio_();
		}
	}
	void FigurePoints::resetSizeRatio_()
	{
		auto x = 0.5f * m_point_scale;

		m_size_ratio = { x, x*(m_scale2.y/m_scale2.x) };
	}
	void FigurePoints::draw()
	{
		if(!m_content) return;
		if(!m_content->getVertexesCount()) return;

		this->updateMatrix();

		this->updateContent();
		
		this->drawR_();
	}

#if ZEN_DRAW_GLES
	void FigurePoints::initR_()
	{
	}
	void FigurePoints::drawR_()
	{
		auto & buffer = m_content->getDrawBuffer();

		auto alpha = DrawStack::Get()->getTopAlpha() * this->getAlpha();
		
		auto fmt = m_texture ? m_texture->format() : ePixel::None;

		auto kit = m_shaders? m_shaders(fmt) : ShaderKitPoints::SelectorNormal()(fmt);

		Helper::Get()->performBlend(m_blend);

		kit->active();

		buffer.v.bind();

		typedef decltype(buffer.v)::VertexData Type;
		kit->bindAttColor(4, GL::eType::Float, 0, sizeof(Type), Type::OffColor);
		kit->bindAttCoord(2, GL::eType::Float, 0, sizeof(Type), Type::OffCoord);

		kit->setUniColor(Vec4{m_color.red, m_color.green, m_color.blue, m_color.alpha*alpha});
		kit->setUniMat4(this->getMatrix());
		kit->setUniPointSizeRatio(m_size_ratio);

		if(m_texture) kit->setUniSampler(1, m_texture->buffer().getID());

		kit->bindAttPointSize(1, GL::eType::Float, 0, sizeof(Type), Type::OffPointSize);

		kit->prepare();
		kit->render(eVMode::Point, 0, buffer.v.count());
	}
	
#elif ZEN_DRAW_METAL
	void FigurePoints::initR_()
	{
		musts(sizeof(MTVBufIN_P) == sizeof(VertexData), "internal error");
	}
	void FigurePoints::drawR_()
	{
		if(m_is_point_size_dirty)
		{
			m_is_point_size_dirty = false;

			auto x = 0.5f * m_point_scale;
			m_size_ratio = { x, x*(m_scale2.y/m_scale2.x) };
			mRBuffer.u.updateSizeRatio(m_size_ratio, m_scale2.x, true);
		}

		if(mRBuffer.is_dirty)
		{
			auto & buf = m_vertexes;
			
			mRBuffer.is_dirty = false;
			
			mRBuffer.v.updateBuffer((MTVBufIN_P*)(void*)buf.data(), buf.size());
		}

		mRBuffer.u.updateMatrix(getMatrix(), getMatrixVersion(), false);
		
		float alpha = getAlpha() * DrawStack::Get()->getTopAlpha();
		mRBuffer.u.updateColor(getColor(), alpha, m_is_color_dirty);
		m_is_color_dirty = false;

		/// start render.
		auto blends = Helper::Get()->getBlends(m_blend);
		
		if( m_texture)
		{
			auto pipe = Metal::ShaderKitPT::GetShared
			(m_is_grey_mode, m_texture->format(), blends);
			kit->render(mRBuffer.u, mRBuffer.v, m_texture->buffer(), eVMode::Point);
			
		}
		else {
			
			auto pipe = Metal::ShaderKitPoints::GetShared(m_is_grey_mode, blends);
			kit->render(mRBuffer.u, mRBuffer.v, eVMode::Point);
		}
	}
#endif
}}


namespace Zen { namespace Vap {
	
	FigureColors::FigureColors()
	{
		this->setContentSize(Activity::Get()->getRealViewSize());
	}
	void FigureColors::setLineMode(bool m)
	{
		m_is_line_mode = m;
	}
	bool FigureColors::isLineMode()
	{
		return m_is_line_mode;
	}

	void FigureColors::setContentSize(Size2 size)
	{
		this->setScale2_(size.w, size.h);
	}
	Size2 FigureColors::getContentSize()
	{
		return { m_scale2.x, m_scale2.y };
	}

	void FigureColors::draw()
	{
		if(!m_content) return;
		if(!m_content->getVertexesCount()) return;

		this->updateMatrix();

		this->updateContent();

		this->drawR_();
	}

#if ZEN_DRAW_GLES

	void FigureColors::initR_()
	{
	}
	void FigureColors::drawR_()
	{
		auto & buffer = m_content->getDrawBuffer();

		auto alpha = DrawStack::Get()->getTopAlpha() * this->getAlpha();

		auto kit = m_shaders?m_shaders(ePixel::RGBA): ShaderKitColors::SelectorNormal()(ePixel::RGBA);

		Helper::Get()->performBlend(m_blend);
		
		kit->active();
		
		buffer.v.bind();

		typedef decltype(buffer.v)::VertexData Type;
		kit->bindAttColor(4, GL::eType::Float, 0, sizeof(Type), Type::OffColor);
		kit->bindAttCoord(2, GL::eType::Float, 0, sizeof(Type), Type::OffCoord);

		kit->setUniColor(Vec4{m_color.red, m_color.green, m_color.blue, m_color.alpha*alpha});
		kit->setUniMat4(this->getMatrix());

		kit->prepare();

		using Mode = GL::eVMode;
		auto mode = m_is_line_mode ?
		(m_content->isStrip() ? Mode::LineStrip : Mode::Line)
		: (m_content->isStrip() ? Mode::TriangleStrip : Mode::Triangle);

		kit->render(mode, 0, buffer.v.count());
	}
	
#elif ZEN_DRAW_METAL
	void FigureColors::initR_()
	{
		musts(sizeof(MTVBufIN_C) == sizeof(VertexData), "internal error");
	}
	void FigureColors::drawR_()
	{
		if(mRBuffer.is_dirty)
		{
			auto & buf = m_vertexes;
			
			mRBuffer.is_dirty = false;
			
			mRBuffer.v.updateBuffer((MTVBufIN_C*)(void*)buf.data(), buf.size());
		}
		
		/// update uniform {

		mRBuffer.u.updateMatrix(getMatrix(), getMatrixVersion(), false);
		
		float alpha = getAlpha() * DrawStack::Get()->getTopAlpha();
		mRBuffer.u.updateColor(getColor(), alpha, m_is_color_dirty);
		m_is_color_dirty = false;
		/// } update uniform

		auto blends = Helper::Get()->getBlends(m_blend);

		using Mode = GL::eVMode;
		auto mode = m_is_line_mode ?
		(m_is_strip? Mode::LineStrip : Mode::Line)
		: (m_is_strip? Mode::TriangleStrip : Mode::Triangle);

		auto pipe = Metal::ShaderKitColors::GetShared(m_is_grey_mode, blends);
		kit->render(mRBuffer.u, mRBuffer.v, mode);
	}
#endif
}}

namespace Zen { namespace Vap {

	FigureTexture::FigureTexture()
	{
		this->setContentSize({100, 100});
	}

	void FigureTexture::setContentSize(Size2 size)
	{
		this->setScale2_(size.w, size.h);
	}
	Size2 FigureTexture::getContentSize()
	{
		return { m_scale2.x, m_scale2.y };
	}

	void FigureTexture::updateContent()
	{
		if(this->updateTexture_() || m_is_flip_dirty) {
			float x0 = m_texture_rect.x * m_texture->using_range().x;
			float y0 = m_texture_rect.y * m_texture->using_range().y;
			float x1 = m_texture_rect.w * m_texture->using_range().x + x0;
			float y1 = m_texture_rect.h * m_texture->using_range().y + y0;
			if(m_is_flip_x) std::swap(x0, x1);
			if(m_is_flip_y) std::swap(y0, y1);
			m_texture_rect_converted = { x0, y0, x1-x0, y1-y0 };

			m_is_flip_dirty = false;
		}
	}

	void FigureTexture::draw()
	{
		if(!m_content) return;
		if(!m_content->getVertexesCount()) return;

		this->updateMatrix();

		this->updateContent();

		this->drawR_();
	}
#if ZEN_DRAW_GLES
	void FigureTexture::initR_()
	{
	}
	void FigureTexture::drawR_()
	{
		auto & buffer = m_content->getDrawBuffer();

		auto alpha = DrawStack::Get()->getTopAlpha() * this->getAlpha();

		auto fmt = m_texture?m_texture->format() : ePixel::None;

		auto kit = m_shaders? m_shaders(fmt) : ShaderKitTexture::SelectorNormal()(fmt);

		Helper::Get()->performBlend(m_blend);

		kit->active();
		buffer.v.bind();

		typedef decltype(buffer.v)::VertexData Type;

		kit->bindAttTexCoord(2, GL::eType::Float, 0, sizeof(Type), Type::OffTexCoord);
		kit->bindAttCoord(2, GL::eType::Float, 0, sizeof(Type), Type::OffCoord);

		kit->setUniColor(Vec4{m_color.red, m_color.green, m_color.blue, m_color.alpha*alpha});
		kit->setUniMat4(this->getMatrix());
		kit->setUniTexRect(m_texture_rect_converted);

		if(m_texture) kit->setUniSampler(1, m_texture->buffer().getID());

		kit->prepare();

		auto mode = m_content->isStrip()?eVMode::TriangleStrip:eVMode::Triangle;
		kit->render(mode, 0, buffer.v.count());
	}
#elif ZEN_DRAW_METAL

	void FigureTexture::initR_()
	{
		musts(sizeof(MTVBufIN_T) == sizeof(VertexData), "internal error");
	}
	void FigureTexture::drawR_()
	{
		if(mRBuffer.is_dirty)
		{
			auto buf = makeVertexDataList_();

			mRBuffer.is_dirty = false;

			mRBuffer.v.updateBuffer((MTVBufIN_T*)(void*)buf.data(), buf.size());
		}

		/// update uniform {

		mRBuffer.u.updateMatrix(getMatrix(), getMatrixVersion(), false);

		float alpha = getAlpha() * DrawStack::Get()->getTopAlpha();
		mRBuffer.u.updateColor(getColor(), alpha, m_is_color_dirty);
		m_is_color_dirty = false;
		/// } update uniform

		typedef Metal::eVMode Mode;
		auto mode = (m_is_strip? Mode::TriangleStrip : Mode::Triangle);

		auto blends = Helper::Get()->getBlends(m_blend);

		auto pipe = Metal::ShaderKitTexture::GetShared(m_is_grey_mode, m_texture->format(),
											blends);

		kit->render(mRBuffer.u, mRBuffer.v, m_texture->buffer(), mode);
	}
#endif
}}

namespace Zen { namespace Vap {
	FigureLoader * FigureLoader::Get() {
		static auto me = new FigureLoader;
		return me;
	}
	SharedFigureTextureContent FigureLoader::createPolygon(size_t edge_count, float start_angle)
	{
		auto content = std::make_shared<FigureTextureContent>();

		float d = start_angle;
		float dd = ZEN_F_2PI / (float)edge_count;
		VertexDataTexture v0 = { { 0, 0 }, { 0.5f, 0.5f } };
		VertexDataTexture v;
		for(size_t i = 0; i < edge_count; ++i) {
			float x = cosf(d);
			float y  =sinf(d);

			content->addVertex(v0);
			content->addVertex(v);
			v.coord = { x, y };
			v.tex_coord = { (x/2.f) + 0.5f, 0.5f - y/2.f };
			content->addVertex(v);
			d += dd;
		}
		content->updateVertex(1, v);
		return content;
	}
	SharedFigureTextureContent FigureLoader::createWheel(size_t edge_count, float direct, float nearR, float farR)
	{
		auto content = std::make_shared<FigureTextureContent>();

		float dd = ZEN_F_PI / (float)edge_count;
		float d = direct;

		VertexDataTexture v;
		for(size_t i = 0; i < edge_count; ++i)
		{
			for(int j = 0; j < 2; ++j) {
				float r = (j?farR:nearR);
				float x = cosf(d) * r;
				float y = sinf(d) * r;
				v.coord = { x, y };
				v.tex_coord = { (x/2.f) + 0.5f, 0.5f - y/2.f };
				content->addVertex(v);
				d += dd;
			}
		}
		content->addVertex(content->getVertex(0));
		content->addVertex(content->getVertex(1));
		content->setStrip(1);
		return content;
	}

	SharedFigureTextureContent FigureLoader::createStar(size_t edge_count, float direct, float nearR, float farR)
	{
		auto content = std::make_shared<FigureTextureContent>();

		float dd = ZEN_F_PI / (float)edge_count;
		float d = direct;

		VertexDataTexture v0 = { { 0, 0 }, { 0.5f, 0.5f } };
		VertexDataTexture v;
		for(size_t i = 0; i < edge_count; ++i)
		{
			for(int j = 0; j < 2; ++j) {
				float x = cosf(d) * (j?nearR:farR);
				float y = sinf(d) * (j?nearR:farR);
				content->addVertex(v0);
				content->addVertex(v);
				v.coord = { x, y };
				v.tex_coord = { (x/2.f) + 0.5f, 0.5f - y/2.f };
				content->addVertex(v);
				d += dd;
			}
		}
		content->updateVertex(1, v);
		content->setStrip(0);
		return content;
	}
}}
